package com.yanfan.modbus.frame;

import com.yanfan.modbus.ModbusUtils;
import com.yanfan.modbus.code.FunctionCode;
import com.yanfan.modbus.exception.ModbusIdException;
import com.yanfan.modbus.exception.ModbusTransportException;
import com.yanfan.modbus.frame.abs.ModbusRequest;
import io.netty.buffer.ByteBuf;


public class WriteMaskRegisterRequest extends ModbusRequest {
    private int writeOffset;

    /**
     * The andMask determines which bits we want to change. If a bit in the andMask is 1, it indicates that the value
     * should not be changed. If it is zero, it should be changed according to the orMask value for that bit.
     */
    private int andMask;

    /**
     * The orMask determines what value a bit will have after writing if the andMask allows that bit to be changed. If a
     * changable bit in the orMask is 0, the bit in the result will be zero. Ditto for 1.
     */
    private int orMask;

    /**
     * Constructor that defaults the masks to have no effect on the register. Use the setBit function to modify mask
     * values.
     */
    public WriteMaskRegisterRequest(int slaveId, int writeOffset) throws ModbusTransportException {
        this(slaveId, writeOffset, 0xffff, 0);
    }

    public WriteMaskRegisterRequest(int slaveId) throws ModbusTransportException {
        super(slaveId);
    }

    public WriteMaskRegisterRequest(int slaveId, int writeOffset, int andMask, int orMask) throws ModbusTransportException {
        super(slaveId);
        this.writeOffset = writeOffset;
        this.andMask = andMask;
        this.orMask = orMask;
    }


    @Override
    public void validate() throws ModbusTransportException {
        ModbusUtils.validateOffset(writeOffset);
    }

    @Override
    public byte getFunctionCode() {
        return FunctionCode.WRITE_MASK_REGISTER;
    }


    public void setBit(int bit, boolean value) {
        if (bit < 0 || bit > 15) {
            throw new ModbusIdException("Bit must be between 0 and 15 inclusive");
        }

        // Set the bit in the andMask to 0 to allow writing.
        andMask = andMask & ~(1 << bit);

        // Set the bit in the orMask to write the correct value.
        if (value) {
            orMask = orMask | 1 << bit;
        } else {
            orMask = orMask & ~(1 << bit);
        }
    }


    @Override
    protected void writeRequest(ByteBuf buf) {
        buf.writeShort(writeOffset);
        buf.writeShort(andMask);
        buf.writeShort(orMask);
    }


    @Override
    protected void readRequest(ByteBuf buf) {
        writeOffset = buf.readUnsignedShort();
        andMask = buf.readUnsignedShort();
        orMask = buf.readUnsignedShort();
    }
}
